#pragma once
#include <cstdlib>
#include <string>
#include <unistd.h>

#include <iostream>
#include <unistd.h>
#include <sys/wait.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/resource.h>

#include "../Comm/Util.hpp"
#include "../Comm/Log.hpp"
namespace ns_runner
{
    using namespace ns_util;
    using namespace ns_log;
    class Runner
    {
    public:
        Runner() {}
        ~Runner() {}

    public:
        static void SetLimit(int cpu_limit, int memo_limit)
        {
            struct rlimit cpu;
            cpu.rlim_cur = cpu_limit;
            cpu.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_CPU,&cpu);

            struct rlimit memo;
            memo.rlim_cur = memo_limit*1024;
            memo.rlim_max = RLIM_INFINITY;
            setrlimit(RLIMIT_AS,&memo);
        }


        /******************
         * 程序运行的结果:
         * 1、程序运行成功，结果正确
         * 2、程序运行成功，结果错误
         * 3、程序运行异常
         * Run函数只关心程序是否运行成功，程序运行结果是否正确由上层来决定
         ******************/
        static int Run(const std::string &filename, int cpu_limit, int memo_limit)
        {
            /*****************
             * 程序返回结果
             * 1、< 0 ，内部错误
             * 2、== 0 ，程序运行成功
             * 3、> 0 ，程序运行时错误
             ******************/
            
            std::string execute = Path_Util::Exe(filename);
            std::string _stdin = Path_Util::StdIn(filename);
            std::string _stdout = Path_Util::StdOut(filename);
            std::string _stderr = Path_Util::StdErr(filename);

            umask(0);
            int _stdin_fd = open(_stdin.c_str(), O_CREAT | O_WRONLY, 0644);
            int _stdout_fd = open(_stdout.c_str(), O_CREAT | O_WRONLY, 0644);
            int _stderr_fd = open(_stderr.c_str(), O_CREAT | O_WRONLY, 0644);

            if (_stdin_fd < 0 || _stdout_fd < 0 || _stderr_fd < 0)
            {
                if(_stdin_fd >= 0) close(_stdin_fd);
                if(_stdout_fd >= 0) close(_stdout_fd);
                if(_stderr_fd >= 0) close(_stderr_fd);
                LOG(ERROR) << "标准文件打开失败\n";
                return -1;
            }
            pid_t pid = fork();

            if (pid < 0)
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                LOG(ERROR) << "子进程创建失败\n";
                return -2;
            }
            else if (pid == 0)
            {
                dup2(_stdin_fd, 0);
                dup2(_stdout_fd, 1);
                dup2(_stderr_fd, 2);

                // 设置cpu运行时间和内存大小限制
                SetLimit(cpu_limit, memo_limit);
                execl(execute.c_str(), execute.c_str(), nullptr);
                LOG(ERROR) << "程序替换失败\n";
                exit(1);
            }
            else
            {
                close(_stdin_fd);
                close(_stdout_fd);
                close(_stderr_fd);
                int status = 0;
                waitpid(pid, &status, 0); // 程序发生运行时错误(异常)，在Linux中必然是收到了信号
                if(WIFEXITED(status))
                    LOG(INFO) << "程序运行成功，info:" << (status & 0x7f) << "\n";
                else
                    LOG(INFO) << "程序运行出错，info:" << (status & 0x7f) << "\n";
                return (status & 0x7f);
            }
        }
    };
}